package cn.pku.eecs.ldn.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.ScrollView;
import android.widget.Scroller;

public class PullableScrollView extends ScrollView {
	private final static String TAG = "PullableScrollView";
	private final static boolean DEBUG = true;

	private final int SCROLL_QUOTIENT = 2;
	private final int SCROLL_TO_TOP = 0;
	private final int SCROLL_TO_BOTTOM = 1;
	private int scrollTo = 0;
	private Scroller mScroller;

	private int originPaddingTop = 0;
	private int originPaddingBottom = 0;

	public PullableScrollView(Context context) {
		super(context);
		init(context);
	}

	public PullableScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public PullableScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	private void init(Context context) {
		mScroller = new Scroller(context, new DecelerateInterpolator());
		originPaddingTop = getPaddingTop();
		originPaddingBottom = getPaddingBottom();
		Log.d(TAG, "init(): originPaddingTop " + originPaddingTop);
		Log.d(TAG, "init(): originPaddingBottom " + originPaddingBottom);
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			Log.d(TAG, "computeScroll(): in animation");
			int y = mScroller.getCurrY();
			if (scrollTo == SCROLL_TO_TOP) {
				setPaddingTop(y);
			} else if (scrollTo == SCROLL_TO_BOTTOM) {
				setPaddingBottom(y);
			} else {
				if (DEBUG)
					Log.e(TAG, "computeScroll(): unknown srcoll");
			}

		} else {
			Log.d(TAG, "computeScroll(): just log");
		}
		super.computeScroll();
	}

	private void setPaddingTop(int paddingTop) {
		if (paddingTop < originPaddingTop) {
			paddingTop = originPaddingTop;
		}
		setPadding(getPaddingLeft(), paddingTop, getPaddingRight(),
				getPaddingBottom());
	}

	private void setPaddingBottom(int paddingBottom) {
		if (paddingBottom < originPaddingBottom) {
			paddingBottom = originPaddingBottom;
		}
		setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
				paddingBottom);
	}

	float lastY = -1;

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastY = ev.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			final float delta = ev.getRawY() - lastY;
			lastY = ev.getRawY();
			Log.d(TAG, "onTouchEvent(): getScrollY " + getScrollY());
			if (getPaddingTop() > originPaddingTop && delta < 0) {
				setPaddingTop((int) (getPaddingTop() + delta));
				return true;
			}
			if (getScrollY() <= 0 && delta > 0) {
				setPaddingTop((int) (getPaddingTop() + delta / SCROLL_QUOTIENT));
				return true;
			}
			if (getPaddingBottom() > originPaddingBottom && delta > 0) {
				setPaddingBottom((int) (getPaddingBottom() - delta));
			} else if (getScrollY() >= (computeVerticalScrollRange() - getHeight())
					&& delta < 0) {
				setPaddingBottom((int) (getPaddingBottom() - delta
						/ SCROLL_QUOTIENT));
			}
			break;
		default:
			if (getPaddingTop() > originPaddingTop) {
				resetPaddingTop();
			} else if (getPaddingBottom() > originPaddingBottom) {
				resetPaddingBottom();
			}

			break;
		}
		return super.onTouchEvent(ev);
	}

	private void resetPaddingTop() {
		Log.d(TAG, "resetPaddingTop()");
		mScroller.startScroll(0, getPaddingTop(), 0, originPaddingTop
				- getPaddingTop());
		scrollTo = SCROLL_TO_TOP;
		invalidate();
	}

	private void resetPaddingBottom() {
		Log.d(TAG, "resetPaddingBottom()");
		mScroller.startScroll(0, getPaddingBottom(), 0, originPaddingBottom
				- getPaddingBottom());
		scrollTo = SCROLL_TO_BOTTOM;
		invalidate();
	}
}
